package com.ljj.admin.shiro.realm;

import com.ljj.admin.common.utils.ShiroUtils;
import com.ljj.admin.mybatis.system.beans.SysUser;
import com.ljj.admin.mybatis.system.services.impl.SysMenusService;
import com.ljj.admin.mybatis.system.services.impl.SysRoleService;
import com.ljj.admin.mybatis.system.services.impl.SysUserService;
import com.ljj.admin.shiro.services.UserPasswordSecureService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.Set;

@Component
public class sysUserRealm extends AuthorizingRealm {

    Logger logger= LoggerFactory.getLogger(sysUserRealm.class);

    @Autowired
    private SysUserService sysUserService;


    @Autowired
    private SysRoleService sysRoleService;


    @Autowired
    private SysMenusService sysMenusService;

    @Autowired
    private UserPasswordSecureService secureService;


    /**
     * 授权用户权限
     * 授权的方法是在碰到<shiro:hasPermission name=''></shiro:hasPermission>标签的时候调用的
     * 它会去检测shiro框架中的权限(这里的permissions)是否包含有该标签的name值,如果有,里面的内容显示
     * 如果没有,里面的内容不予显示(这就完成了对于权限的认证.)
     *
     * shiro的权限授权是通过继承AuthorizingRealm抽象类，重载doGetAuthorizationInfo();
     * 当访问到页面的时候，链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行
     * 所以如果只是简单的身份认证没有权限的控制的话，那么这个方法可以不进行实现，直接返回null即可。
     *
     * 在这个方法中主要是使用类：SimpleAuthorizationInfo 进行角色的添加和权限的添加。
     * authorizationInfo.addRole(role.getRole()); authorizationInfo.addStringPermission(p.getPermission());
     *
     * 当然也可以添加set集合：roles是从数据库查询的当前用户的角色，stringPermissions是从数据库查询的当前用户对应的权限
     * authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(stringPermissions);
     *
     * 就是说如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[权限添加]");
     * 就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问
     *
     * 如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002]，perms[权限添加]");
     * 就说明访问/add这个链接必须要有 "权限添加" 这个权限和具有 "100002" 这个角色才可以访问
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        SysUser sysUser = ShiroUtils.getSysUser();
        SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();

        if("admin".equalsIgnoreCase(sysUser.getLoginName()))
        {
            //如果是管理员登录  则默认全部权限
            info.addRole("admin");
            info.addStringPermission("*:*:*");
        }else
        {
            Set<String> primSet = sysRoleService.selectRoleKeyByUserId(sysUser.getUserId());
            info.addRoles(primSet);

            Set<String> permsString = sysMenusService.selectPermsByUserId(sysUser.getUserId());
            info.addStringPermissions(permsString);
        }

        return info;
    }


    /**
     * 登录验证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;
        String password="";
        if(token.getPassword()!=null)
        {
            password=new String(token.getPassword());
        }else
        {
            throw new IncorrectCredentialsException("密码不能为空");
        }

        //根据用户名 获取用户信息 如果存在用户信息 开始比对密码
        SysUser sysUser = sysUserService.userLoginOn(token.getUsername(), token.getPassword().toString());

        //根据用户名无法获取 用户信息
        if(ObjectUtils.isEmpty(sysUser))
        {
            //返回用户为空 异常
            //未知的账号
            throw new UnknownAccountException("无效用户名");
        }

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                sysUser, //用户
                sysUser.getPassword(), //密码
                ByteSource.Util.bytes(sysUser.getSalt()),//salt=username+salt
                getName() //realm name
        );
        return authenticationInfo;
    }


    @Override
    protected Object getAuthenticationCacheKey(AuthenticationToken token) {
        return super.getAuthenticationCacheKey(token);
    }


    @Override
    protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
        return super.getAuthorizationCacheKey(principals);
    }
}
